
#include <BRender>
#include <BKeyEvent>
#include <BMouseEvent>
#include <BSphereHandler>

#include <BCamera>
#include <BViewer>
#include <BShape>
#include <BMaterial>
#include <BOpenGL>
#include <BSystem>
#include <BScene>

#include "MainHandler.h"
#include "DragHandler.h"

DragHandler::DragHandler()
{
	BSphereHandler* sphereHandler = new BSphereHandler();
	this->addChild(sphereHandler);

	_leftPressed = false;
	_spaceShown = true;
	_pickedConstraint = 0;
}
DragHandler::~DragHandler()
{
	removePickingConstraint();
}

void DragHandler::freshDragRing(BReal axisSize)
{
	BReal unit = 2 * PI / 24;
	_dragRingPoints.resize(50);
	for (int i = 0; i < 25; i++)
	{
		BReal r = i * unit;
		BVector v0(cos(r) * axisSize, sin(r) * axisSize, 0);
		BVector v1(cos(r) * axisSize / 2, sin(r) * axisSize / 2, 0);
		_dragRingPoints[i * 2 + 0] = v0;
		_dragRingPoints[i * 2 + 1] = v1;
	}
}

void DragHandler::removePickingConstraint()
{
	if (_pickedConstraint && _pickedNode)
	{
		btRigidBody* pickedBody = _pickedNode->asset();
		pickedBody->setActivationState(_savedState);
		pickedBody->activate();
		if (btDiscreteDynamicsWorld* dynamicsWorld = this->scene()->asset())
		{
			dynamicsWorld->removeConstraint(_pickedConstraint);
		}
		_pickedConstraint = 0;
	}
}

void DragHandler::render(BRender& render)
{
	if (_pickedNode)
	{
		BMatrix matrix;
		matrix.setPosition(_pickedNode->space().center());
		BQuater rotation(BVector(0, 0, 1), -this->camera()->direct());
		matrix.setRotate(_pickedNode->matrix().rotate() * rotation);
		render.setMatrix(matrix);

		render.setLighting(false);
		render.setDepthTest(false);
		//glDepthFunc(GL_ALWAYS);

		if (_leftPressed)
			render.setColor(255, 255, 0, 100);
		else
			render.setColor(255, 0, 0, 100);
		render.fillQuadStrip(_dragRingPoints);

		render.setLighting(true);
		render.setDepthTest(true);
		render.setColor(100, 168, 250);
		render.drawLine(_dragLine);
	}
	if (_debugPoint != BVector())
	{
		render.setLighting(false);
		render.setPointSize(6);
		render.setColor(255, 0, 0);
		render.drawPoint(_debugPoint);
	}
	if (_debugLine.length() > 0.001f)
	{
		render.setLineWidth(3);
		render.setColor(255, 255, 255);
		render.drawLine(_debugLine);
	}
}

void DragHandler::attachEvent(const BEvent& event)
{
	removePickingConstraint();
}
void DragHandler::detachEvent(const BEvent& event)
{
	removePickingConstraint();
}

bool DragHandler::mousePressEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		const BPoint& pos = mouseEvent.pos();
		BPickItem* item = this->viewer()->pick(pos);
		if (item)
		{
			_pickedNode = item->node();
			_pickedPoint = item->absolutePoint();
			if (_pickedNode)
			{
				btVector3 pickPos(_pickedPoint.x(), _pickedPoint.y(), _pickedPoint.z());
				btDiscreteDynamicsWorld* dynamicsWorld = this->scene()->asset();
				if (btRigidBody* body = _pickedNode->asset())
				{
					if (!(body->isStaticObject() || body->isKinematicObject()))
					{
						_savedState = body->getActivationState();
						body->setActivationState(Disable_Deactivation);
						btVector3 localPivot = body->getTransform().inverse() * pickPos;
						if (!_pickedConstraint)
							_pickedConstraint = new btPointConstraint(body, localPivot);
						_pickedConstraint->setImpulseClamp(30);
						_pickedConstraint->setTAU(0.1);
						dynamicsWorld->addConstraint(_pickedConstraint);
					}
				}
				BLine line = this->viewer()->line(pos);
				_oldPickingDist = (_pickedPoint - line.begin()).length();
				_hitPos = pickPos;
			}
			_leftPressed = true;
		}
	}
	return true;
}
bool DragHandler::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		removePickingConstraint();
	}
	return true;
}
bool DragHandler::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	BLine line = this->viewer()->line(pos);
	if (_leftPressed && _pickedNode && _pickedConstraint)
	{
		const BVector& begin = line.begin();
		const BVector& end = line.end();
		btVector3 rayFrom(begin.x(), begin.y(), begin.z());
		btVector3 rayTo(end.x(), end.y(), end.z());
		btVector3 dir = rayTo - rayFrom;
		dir.normalize();
		dir *= _oldPickingDist;
		btVector3 newPivotB = rayFrom + dir;
		_pickedConstraint->setPointB(newPivotB);
	}
	return true;
}

